/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Simulator of machine that would operate on ICE2 instructions. Also performs slight
 * error checking and correction.
 * checks.
 * @author khhill
 */
package Simulator;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;


public class Control{
    public static String internal_debug = "ON";
    
    public static String[] code_string = new String[0];
    public static String[] read_string = new String[0];
    public static int read_counter = 0;
    public static int read_size = 0;
    public static int location_counter, starting_location,max_location;
    public static int line_num;
    public static String debug_status = "OFF";
    public static String code_file = "src//Simulator//sim_code.txt";
    public static String read_file = "src//Simulator//read_file.txt";
    
    public static String version_number = "SP4";
    public static String revision_number = "alpha1";
    public static String[] memory = new String[1024];
    public static String[] debug = new String[1024];
    public static boolean finished = false;
    public static int loc = 0; 
    public static int execution_count = 0;
    
   /**
    * Dumps the contents of the program memory to the screen. The range of memory 
    * to be output will be from the program starting location to the last location
    * in memory that is used.
    * <p>
    * <b>Change Log:</b><br>
    * 05/24/2008 - Initial Code - Peter Dietz<br>
    * <p>
    * <b>Error Conditions Tested:</b><br>
    * no errors to test
    * <p>
    * <b>Date of module being installed:</b> 05/24/08
    * <p><b>Original Author:</b>  Peter Dietz
    */
    public void dumpMem(){
        IceAssembly.Convert convert_instance = new IceAssembly.Convert();
        for(int i=Control.starting_location; i<=Control.max_location; i=i+16){
            String memory_output_row = "MEM "+convert_instance.intToFourHex(i).substring(1)+" ";
            for(int j=i; (j < i+16) && (j <= Control.max_location); j++){
                memory_output_row = memory_output_row+Control.memory[j]+" ";
            }
            System.out.println(memory_output_row);
        }
    }    
    
    /**
     * This function initializes code_string[] from the code file
     * whose URL String is declared in the Interface.java.<p>
     * <b>Change Log:</b><br>
     * 05/17/2008 - Modified Yogesh's code for use in Simulator - Keith Hill<p>
     * <b>Errors produced:</b><br>
     * INTERNAL File reading IO error<p>
     * <b>Date of module being installed:</b> 5/17/08
     * <p><b>Original Author:</b> Keith Hill
     * @return Returns 1 if successful else returns 0.
    **/ 
    private int getCodeFile () {
        try{
            File file = new File(code_file);
            BufferedReader buffer_reader = new BufferedReader(new FileReader(file));
            int no_lines=0;
            while(buffer_reader.readLine()!=null){
               no_lines++;
            }
            code_string = new String[no_lines]; 
            buffer_reader = new BufferedReader(new FileReader(file));
            int index=0; String temp;
            while((temp=buffer_reader.readLine())!=null){
                code_string[index]= temp; index++;
            }
           
        }catch(IOException e){
            System.out.println("file reading exception from Control.java.getcodefile(); function");
            return 0;
        }
        return 1;
    }
    
    /**
     * Read from the read-file which is used for input to MOPER and SOPER read 
     * instruction. The data will put into a string array, so that additional read 
     * instructions won't have to go to the read-file file itself for read-file data.
     * <b>Change Log:</b><br>
     * 05/24/08 - Modified for use for current task from Yogesh's code - Keith Hill<p>
     * 05/24/08 - Added Documentation - Peter Dietz<br>
     * Errors produced:<br>
     * INTERNAL: Input/Output File reading error<p>
     * Date of module being installed: 5/24/08
     * <p><b>Original Author:</b> Keith Hill
     * @return Returns 1 if successful else returns 0.
    **/ 
    private int getReadFile () {
        try{
            File file = new File(read_file);
            BufferedReader buffer_reader = new BufferedReader(new FileReader(file));
            int no_lines=0;
            while(buffer_reader.readLine()!=null){
               no_lines++;
            } 
            read_size = no_lines;
            read_string = new String[no_lines]; 
            buffer_reader = new BufferedReader(new FileReader(file));
            int index=0; String temp;
            while((temp=buffer_reader.readLine())!=null){               
                read_string[index]= temp; index++;
            }
            
        }catch(IOException e){
            System.out.println("file reading exception from Control.java.getcodefile(); function");
            return 0;
        }
        return 1;
    }
    
    /**
     * The processCode function will process the string array generated from the code file.  It will first 
     * load everything into a simulated global memory array and will then process each line of code, as dicated 
     * by the location counter.
     * <p>
     * <b>Change Log:</b><br>
     * None<br>
     * <p>
     * <b>Error Conditions Tested:</b><br>
     * 007 - Invalid Linker Header<br>
     * 006 - Location Counter out of range<br>
     * 010 - Attempting to execute code at an undefined location in memory.  Terminating Execution.<br>
     * <p>
     * <b>Date of module being installed:</b> 5/17/08
     * <p><b>Original Author:</b>  Keith Hill 
     */
  public static void processCode(){
      Control control_instance = new Control();
      
      // initialize memory
      for(int counter=0; counter<1024; counter++){
          memory[counter] = "x";
      }      
      
      
      if(control_instance.processHeaderRecord(code_string[0]) != 1 ) return;//fatal error
      int record_index = control_instance.processTextRecords();
      control_instance.processEndRecord(record_index);      

      control_instance.dumpMem();
      control_instance.simulateExecution();
      control_instance.dumpMem();      
  }  
  
  /**
   * Processes header record of load file and set starting location and other memory limits. 
   * Also looks for errors with the header record
   * <br>
   * A handy reference guide to the expected Header Record
   * 0 - LH
   * 1 - exec start
   * 2 - module name
   * 3 - length of module
   * 4 - initial program load addr
   * 5 - date
   * 6 - time
   * 7 - ICE2-LINK
   * 8 - Vers #
   * 9 - Rev #
   * <p>
   * <b>Change Log:</b><br>
   * 5/17/2008 - Initial Code - Keith Hill<br>
   * 5/24/2008 - Modularized program - Peter Dietz<br>
   * <p>
   * <b>Error Conditions Tested:</b><br>
   * 006 - Location Counter out of range<br>
   * 007 - Not a valid link header<br>
   * 216 - Didn't specify that it was an ICE2 link record<br>
   * 217 - Other link header record error with the syntax<br>
   * <p>
   * <b>Date of module being installed:</b> //2008
   * <p><b>Original Author:</b>  Peter Dietz
   * @param header_string LH Linker Header record
   * @return int of success. 1 is good, other than 1 mean error
   */
  private int processHeaderRecord(String header_string){
      IceAssembly.Initialize initialize_instance = new IceAssembly.Initialize();
      int header_status = 1;
      
      String[] tokenized_header = header_string.split("\\|");
      
      if(tokenized_header.length != 10){
          initialize_instance.printErrorMessage("007"); // invalid linker header
          header_status++;
      } else{
          if(!tokenized_header[0].toUpperCase().contentEquals("LH")){
            initialize_instance.printErrorMessage("007"); // invalid linker header
            header_status++;
          }
          if(!tokenized_header[7].toUpperCase().contentEquals("ICE2-LINK")){
            initialize_instance.printErrorMessage(216); // not ICE2-LINK
          }
          if( (tokenized_header[5].length() != 8) || (tokenized_header[6].length() !=8) || (tokenized_header[8].length()==0) || (tokenized_header[9].length()==0) ){
              initialize_instance.printErrorMessage(217); // other header error
          }
      }
      
      location_counter = Integer.parseInt(tokenized_header[1], 16);
      starting_location = location_counter;
      max_location = location_counter;
      if(location_counter > 1023){
            initialize_instance.printErrorMessage("006");
            header_status++;
      } 
      
      return header_status;
  }
  
    /**
    * Processes all the text records in the load module file and adds them to memory, 
    * additionally it checks for errors
    * <p>
    * <b>Change Log:</b><br>
    * 5/17/2008 - Initial Code - Keith Hill<br>
    * 5/24/2008 - Made program easier to read by moving things into seperate operations - Peter Dietz
    * <p>
    * <b>Error Conditions Tested:</b><br>
    * 006 - Location Counter out of Range<br>
    * <p>
    * <b>Date of module being installed:</b> 05/17/2008
    * <p><b>Original Author:</b>  Keith Hill 
    * @return index of last record processed
    */
  public int processTextRecords(){
      int index = 1;
      while(code_string[index].toUpperCase().startsWith("LT|")){
          String[] token = code_string[index].substring(3).split("\\|"); //remove LT and tokenize
          
          loc = Integer.parseInt(token[0],16);
          if(loc<=1023 && loc >=0){
            memory[loc] = token[1];
            debug_status = debug[loc] = token[2];
            if(loc>max_location){
                max_location = loc;
            }
            System.out.println("Loading:"+loc + " " + memory[loc]+ " " + debug[loc]);
          }
          else{
              new IceAssembly.Initialize().printErrorMessage("006"); // loc counter out of range
              break;
          }
          
          index++;
      }
      return index;
  }
  
   /**
    * Processes the Linker End Record LE and checks for total num records errors.
    * <p>
    * <b>Change Log:</b><br>
    * 5/25/2008 - Initial Code - Peter Dietz<br>
    * <p>
    * <b>Error Conditions Tested:</b><br>
    * 218 - No Linker End (LE) record found<br>
    * 219 - LE record had incorrect total number of records
    * <p>
    * <b>Date of module being installed:</b> 05/25/2008
    * <p><b>Original Author:</b>  Peter Dietz
    * @param index Index to the row where the end record is at
    */
  public void processEndRecord(int index){
      if (code_string[index].toUpperCase().startsWith("LE|")){
          int total_records = Integer.parseInt(code_string[index].substring(3),16);
          
          if(total_records != index+1){
              new IceAssembly.Initialize().printErrorMessage(219); //incorrect total num records
          }
      } else {
          new IceAssembly.Initialize().printErrorMessage(218); //no LE record found
          
      }
  }
  
  /**
   * Performs the operation simulation of the ICE2 instructions. Makes the calls 
   * to the functions that handle specific instructions.
   * <p>
   * <b>Change Log:</b><br>
   * 5/17/2008 - Initial Code - Keith Hill
   * 5/25/2008 - Moved code into modular functions - Peter Dietz<br>
   * <p>
   * <b>Error Conditions Tested:</b><br>
   * 010 - Infinite or sufficiently large loop detected<br>
   * <p>
   * <b>Date of module being installed:</b> 05/17/2008
   * <p><b>Original Author:</b> Keith Hill
   */
  public void simulateExecution(){
      Debug debug_instance = new Debug();
      
      while(!finished){
          if(memory[location_counter].equals("x")){
              System.out.println("Error 010: Attempting to execute code at an undefined location in memory.  Terminating Execution.");
              finished = true;
              break;
          }
          execution_count++;
          if(execution_count>2000){
              System.out.println("Infinate Loop detected.  Terminating");
              finished = true;
          }
        int code = Integer.parseInt(memory[location_counter],16);
        String binary = Integer.toBinaryString(code);
        while(binary.length()<16){
            binary = "0"+ binary;
        }
        
        //BEFORE DEBUG
        if(debug[location_counter].equals("ON") && !memory[location_counter].equals("x")){
            debug_instance.beforeSimulation();
            debug_status="ON";
        }
        
        
        if(binary.startsWith("00")){
            Cntl.Control(binary);
        }
        else if(binary.startsWith("01")){
            Jump.Control(binary);   
        }
        else if(binary.startsWith("10")){
            Soper.Control(binary);
        }
        else if(binary.startsWith("11")){
            Moper.Control(binary);
        }
        else{
            location_counter++;
        }
        
        //AFTER DEBUG
        if(debug_status.equals("ON") && !memory[location_counter].equals("x")) {
            debug_instance.afterSimulation(); 
            debug_status = "OFF";
        }
      } 
  }
  
    /**
     * The main will read the file whose url is in the Interface.java.
     * Command line file reading still needs to be done.<p>
     * Change Log:<br>
     *  None<p>
     * Errors produced:<br>
     * None<p>
     * Date of module being installed: 5/17/08
     * <p>
<b>Original Author:</b> Keith Hill
<p>
     * @param args takes the input file from command line(may be)...
     */
    public static void main(String[] args){
        //Convert convert_instance = new Convert();
        //Tokenize tokenize_instance = new Tokenize();
        //Process process_instance = new Process();
        Control control_instance = new Control();
        
        if(internal_debug.contentEquals("ON")){
            new IceAssembly.Control().setInternalDebuggingParameters();
        }
        
        Cntl cntl_instance = new Cntl();
        if(args.length>0){
            code_file = args[0]; 
        }
        
        control_instance.getReadFile();//data passed in by user
        
        if(control_instance.getCodeFile() == 1){
           processCode();
        }

    }
}
